/**
 * 代理与反射: Proxy Reflect
 */

const target = {
  name: 'a',
};
const handler = {
  get(targetObj, property, proxyObj) {
    // console.log(target === targetObj);
    // console.log(property);
    // console.log(proxy === proxyObj);
    return Reflect.get(...arguments);
  },
  set(targetObj, property, value, proxyObj) {
    return Reflect.set(...arguments);
  },
  has(targetObj, property) {
    return Reflect.has(targetObj, property);
  },
  defineProperty(targetObj, property, descriptor) {
    return Reflect.defineProperty(...arguments);
  },
  getOwnPropertyDescriptor(targetObj, property) {
    return Reflect.getOwnPropertyDescriptor(...arguments);
  },
  deleteProperty(targetObj, property) {
    return Reflect.deleteProperty(...arguments);
  },
  ownKeys(targetObj) {
    // 拦截的操作：Object.getOwnPropertyNames({}), Object.keys(), Reflect.ownKeys();
    return Reflect.ownKeys(...arguments);
  },
  getPrototypeOf(target) {
    // Object.getPrototypeOf/ obj instance Object / obj.__proto__ /
    return Reflect.getPrototypeOf(...arguments);
  },
  setPrototypeOf(target) {
    Object.getPrototypeOf({}, { name: '' });
    // Object.getPrototypeOf/ obj instance Object / obj.__proto__ /
    return Reflect.setPrototypeOf(...arguments);
  },
  apply(targetFunc, thisVal, ...args) {
    // call/apply/Reflect.apply
    return Reflect.apply(...arguments);
  },
  construct(Func, ...args) {
    return Reflect.construct(...arguments);
  },
  isExtensions(targetObj) {
    // Object.isExtensible
    // 判断对象是否不可扩展，即不可以修改
    return Reflect.isExtensible(...arguments);
  },
  preventExtensions() {
    // Object.preventExtensions
    return Reflect.preventExtensions(...arguments);
  },
};
const proxy = new Proxy(target, handler);
proxy.name = '11';
console.log(proxy.name);

// 反射提供的一些函数
// 1 提供带有返回状态的函数，表面这次操作是否成功
const log = console.log;
log(Reflect.defineProperty(target, 'age', { value: 1 }));
log(Reflect.deleteProperty(target, 'name'));
log(Reflect.set(target, 'name1', 'name'));
// log(Reflect.setPrototypeOf(target, {}));
Reflect.preventExtensions;
log(target);
log(target.age);

function Person() {
  this.name = 'person';
}
Reflect.get; // 代替取值操作符
Reflect.set; // 代替赋值操作符
Reflect.deleteProperty;
Reflect.has; // 代替in操作符
Reflect.apply(Person, {}, {});
// const o = Reflect.construct(); // 代替new操作符
log();

/**
 * 代理可以拦截的方式
 * 1 跟踪属性访问（依赖收集）
 * 2 隐藏属性访问（可以对特定的属性不支持访问，返回undefined）
 * 3 属性验证（对不符合的属性不给予赋值等操作）
 * 4 函数和构造函数参数验证
 * 函数执行时，可以控制函数传入的个数或者类型，构造函数实例化时，控制入参
 */
// apply/construct的用法，其实代理了普通函数和构造函数
function Func(num) {}
function Person1(name) {
  this.name = name;
}
const funcProxy = new Proxy(Func, {
  apply(target, thisVal, argsList) {
    console.log(target === Func);
    console.log(thisVal);
    console.log(argsList);
    if (typeof argsList[0] !== 'number') {
      throw new Error('参数必须传入数字');
    }
    return Reflect.apply(...arguments);
  },
});
const pProxy = new Proxy(Person1, {
  construct(target, argsList, proxyTarget) {
    console.log('------------');
    console.log(target === Person1);
    console.log(argsList);
    console.log(proxyTarget);

    if (argsList[0] === undefined) {
      throw new Error('必须传入参数');
    }
    return Reflect.construct(...arguments)
  },
});
funcProxy(1);
new pProxy(1);

// 函数的内部属性：arguments/arguments.callee/new.target/func.caller/
function Func1() {
  console.log(new.target);
  if (new.target === Func1) {
    console.log('new 调用');
  }
  if (!new.target) {
    console.log('正常函数调用')
  }
}